/*
 * Name: main.c
 * Project: elektor HiD Temperature Modul
 * Author: Michael Odenwald
 * Creation Date: 10.05.2013
 *
 */

/* Atmel AVR header files */
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>  /* for sei() */
#include <util/delay.h>     /* for _delay_ms() */
#include <avr/eeprom.h>
#include <string.h> 

/* the obdev USB stack header files */
#include <avr/pgmspace.h>   /* required by usbdrv.h */
#include "usbdrv.h"
#include "oddebug.h"        /* This is also an example for using debug macros */
#include "osccal.h" 
#include "Dio.h"
#include "Display.h"

/* Device globals */
#define STATUS_LED1_PIN PB3
#define STATUS_LED1_PORT PORTB
#define STATUS_LED1_DDR DDRB


/* Sensor Definitions */
#define MAXSENSORS 5

/* Timing Definitions */
#define MAIN_DELAY_MS 40						// needed by usb stack

#ifndef F_CPU
	#error F_CPU unkown
#endif

/*
 * The USB Hid report descriptor
 */
PROGMEM char usbHidReportDescriptor[33] =
{    
    0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x85, 0x0a,                    //   REPORT_ID (10)
    0x95, 0x04,                    //   REPORT_COUNT (4)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, 0x14,                    //   REPORT_ID (20)
    0x95, 0x0a,                    //   REPORT_COUNT (10)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)			
    0xc0                           // END_COLLECTION
};

 /* UsbDeviceVersion String */
const char UsbDeviceVersion[] = "2013-05-10";

/* The following variables store the status of the current data transfer */
static uchar    currentAddress;
static uchar    bytesRemaining, currentPosition;

static uchar    replyBuf[16];	/* reply buffer should be big enough to cover all data */
static uchar	recBuf[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

static uchar dataReceivedFlag = 0;

/*
 * usbFunctionRead() is called when the host requests a chunk of data from
 * the device. For more information see the documentation in usbdrv/usbdrv.h.
 */
uchar usbFunctionRead(uchar *data, uchar len)
{
    if(len > bytesRemaining)
        len = bytesRemaining;
    eeprom_read_block(data, (uchar *)0 + currentAddress, len);
    currentAddress += len;
    bytesRemaining -= len;
    return len;
}

/*
 * usbFunctionWrite() is called when the host sends a chunk of data to the
 * device. For more information see the documentation in usbdrv/usbdrv.h.
 */
uchar usbFunctionWrite(uchar *data, uchar len)
{
    uchar i;
    if(len > bytesRemaining)                // if this is the last incomplete chunk
        len = bytesRemaining;               // limit to the amount we can store
    bytesRemaining -= len;
    for(i = 0; i < len; i++)
        recBuf[currentPosition++] = data[i];

    if(bytesRemaining == 0)
    	dataReceivedFlag = 1;
    return bytesRemaining == 0;             // return 1 if we have all data
}

/*
 * usbFunctionSetup() is the function which process the commands and send the result back to the microcontroller over USB.
 */
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
	usbRequest_t *rq = (void *)data;
	usbMsgPtr = replyBuf;

    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) /* HID class request */
	{
        if(rq->bRequest == USBRQ_HID_SET_REPORT) /* HID Set Report not implemented yet */
		{
            currentPosition = 0;                // initialize position index
            bytesRemaining = rq->wLength.word;  // store the amount of data requested
            if(bytesRemaining > sizeof(recBuf)) // limit to buffer size
                bytesRemaining = sizeof(recBuf);
            return USB_NO_MSG;
        }
		else if(rq->bRequest == USBRQ_HID_GET_REPORT) /* HID Get Report */
		{
			if(rq->wValue.bytes[0] == 10)												// ReportID 10 read temerature from sensors
			{
				//IO_SET(LED_DEBUG);
				memcpy(&replyBuf[0], &rq->wValue.bytes[0], sizeof(uchar));				// copy report id
				replyBuf[4] =  0;
				replyBuf[3] = 0;
				replyBuf[2] = 0;
				replyBuf[1] = 0;								// copy tval to output buffer
	            return 5;  																// 4 byte tval + 1 byte ID
            }
			else if(rq->wValue.bytes[0] == 20)											// ReportID 20 read UsbDeviceVersion
			{
				memcpy(&replyBuf[0], &rq->wValue.bytes[0], sizeof(uchar));				// copy report id
				memcpy(&replyBuf[1], &UsbDeviceVersion, sizeof(UsbDeviceVersion));		// copy UsbDeviceVersion to output buffer
	            return 11;  															// 10 byte version + 1 byte ID
            }
		}
	}
	else
	{
		// ignore vendor type requests, we don't use any
    }
    return 0;
}

/*
 * calibrateOscillator() calibrate AVR's internal RC oscillator so that the CPU runs at F_CPU
 */
void hadUsbReset(void)
{
	calibrateOscillator();
}

/*
 * main()
 *
 * The start point of the elektor HiD Temperature Modul firmware
 */
int main(void)
{
	uint8_t i;

    wdt_enable(WDTO_1S);
    /* Even if you don't use the watchdog, turn it off here. On newer devices,
     * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
     */
	 
    /* RESET status: all port bits are inputs without pull-up.
     * That's the way we need D+ and D-. Therefore we don't need any
     * additional hardware initialization.
     */
	

    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
    i = 0;
    while(--i){             /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
    i = 0;
    while(--i){             /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
    usbDeviceConnect();
    usbInit();

    sei();

	Display_SelfTest();
	Display_Off();
	
    for(;;) /* main event loop */
	{                
        wdt_reset();
        usbPoll();
		_delay_ms(MAIN_DELAY_MS);

		if(dataReceivedFlag == 1)
		{
			dataReceivedFlag = 0;
			Display_Number(recBuf[1] << 8 | recBuf[2]);
		}

    }
    return 0;
}

